Class Puzzle2048 extends Box
var body;
var map;
var scoreLabel;
var bestLabel;
var moveLocked=false;
var isWin=false;
function Puzzle2048(){
	G.main.addChild(this);
	this.set({pos:[0,0,"f","f"],bg:"#fff"});
	this.addChild([
		[{dim:"body",pos:["c","c+30",584,684]},
			[["2048","#666",90],{pos:["c",-120],bold:true,textShadow:"#4000"}],
			[{pos:[0,0,"f","f"],bg:["#f6f6f6","2#666",9],shadow:true}],
			[{pos:[2,2,"f-4",100],bg:["#e6e6e6",[,"#888"],[9,9]]}],
			[["BEST :","#888",30],{pos:[40,36]}],
			[["0","#444",30],{dim:"bestLabel",x:"+5"}],
			[["SCORE :","#888",30],{pos:[320,36]}],
			[["0","#444",30],{dim:"scoreLabel",x:"+5"}],
			[{dim:"map",pos:[2,102,580,580],bg:[,"#888","shape",
				[145,0],[145,580],0,[290,0],[290,580],0,[435,0],[435,580],0,
				[0,145],[580,145],0,[0,290],[580,290],0,[0,435],[580,435]
			]}],
		],
	]);
	this.start();
	this.playTime();
}
function start(){
	this.isWin=false;
	this.bestLabel.setText(int(IO.getStorage("2048.best")));
	this.scoreLabel.setText(0);
	this.map.removeChildren();
	this.createTile(0,2);
	this.createTile(2,2);
}
function createTile(place=-1,score=2){
	if(place==-1){
		var fits=[];
		for(var y=0;y<4;y++){
			for(var x=0;x<4;x++){
				var place=y*10+x;
				if(!this.getTile(place)) fits.push(place);
			}
		}
		place=fits[int(fits.length*Math.random())];
	}
	var size=score>999?40:(score>99?50:(score>9?60:80));
	var tile=this.map.addChild(new Label(score,"#888",size),0);
	tile.set({score:score,merging:false});
	tile.set({size:[120,120],lineH:120,bold:true,align:"c",pivot:"c",bg:["#fff","2#888",6]});
	this.setPlace(tile,place);
	tile.transit({scale:[0.5,1],ease:1,time:200});
	return tile;
}
function getTile(place){
	for(var i=0;i<this.map.children.length;i++){
		var tile=this.map.children[i];
		if(tile.score>0&&tile.place==place) return tile;
	}
	return null;
}
function setPlace(tile,place){
	tile.place=place;
	tile.setPos((place%10)*145+72,int(place/10)*145+72);
}
function move(dir){
	var moved=false;
	var js=[0,1,2,3];
	if(dir%2==0) js.reverse();
	for(var i=0;i<4;i++){
		for(var i2=0;i2<js.length;i2++){
			var j=js[i2];
			var place=dir<=2?i*10+j:j*10+i;
			var tile=this.getTile(place);
			if(!tile) continue;
			for(var i3=js.length-1;i3>=0;i3--){
				var n=js[i3];
				if(dir%2==1&&n>=j) continue;
				if(dir%2==0&&n<=j) continue;
				var place2=dir<=2?i*10+n:n*10+i;
				if(!this.testMove(tile,place2)) break;
			}
			if(tile.place!=place) moved=true;
		}
	}
	for(var i=0;i<this.map.children.length;i++){
		var tile=this.map.children[i];
		tile.merging=false;
	}
	if(moved) this.createTile();
	this.testLose();
}
function testMove(tile,place){
	var blockTile=this.getTile(place);
	if(blockTile){
		if(blockTile.merging||blockTile.score!=tile.score) return false;
		this.scoreLabel.setText(int(this.scoreLabel.text)+tile.score);
		this.bestLabel.setText(Math.max(int(this.bestLabel.text),int(this.scoreLabel.text)));
		IO.setStorage("2048.best",this.bestLabel.text);
		tile.score=0;
		blockTile.score*=2;
		blockTile.merging=true;
	}
	this.map.addChild(tile);
	tile.transit({ease:1,time:200},[this,this.toMove,tile,blockTile]);
	this.setPlace(tile,place);
	return !blockTile?true:false;
}
function toMove(tile,blockTile){
	if(!blockTile) return;
	tile.remove();
	var score=blockTile.score;
	if(score>0){
		blockTile.setFontSize(score>999?40:(score>99?50:(score>9?60:80)));
		blockTile.setText(score);
		blockTile.transit({scale:[0.5,1],time:200});
	}
	else{
		blockTile.remove();
	}
	if(!this.isWin&&blockTile.score==2048){
		this.isWin=true;
		callLater([DlgAlert,"You win!\n\nClick button to continue."],250);
	}
}
function testLose(){
	var canMerge=false;
	var idleNum=16;
	for(var i=0;i<this.map.children.length;i++){
		var tile=this.map.children[i];
		if(tile.score==0) continue;
		idleNum--;
		var x=tile.place%10;
		var y=int(tile.place/10);
		for(var j=0;j<this.map.children.length;j++){
			var tile2=this.map.children[j];
			if(tile2.score==0) continue;
			var x2=tile2.place%10;
			var y2=int(tile2.place/10);
			if(tile.score!=tile2.score) continue;
			if(y2==y&&(x2==x-1||x2==x+1)) canMerge=true;
			if(x2==x&&(y2==y-1||y2==y+1)) canMerge=true;
		}
	}
	if(idleNum==0&&!canMerge) callLater([this,this.lose],250);
}
function lose(){
	new DlgAlert("You lose!\n\nClick button to restart.",[this,this.start]);
}
function playTime(){
	callLater([this,this.playTime]);
	if(!G.main.enabled) return;
	if(G.isPress){
		if(!this.moveLocked&&G.isPressMove){
			var isMoveX=Math.abs(G.x-G.pressX)>Math.abs(G.y-G.pressY);
			this.move(isMoveX?(G.x<G.pressX?1:2):(G.y<G.pressY?3:4));
			this.moveLocked=true;
		}
	}
	else{
		this.moveLocked=false;
	}
}